package cn.gcjxsc.payment.service.wx;


import cn.gcjxsc.common.data.wx.CloseOrder;
import cn.gcjxsc.common.data.wx.QueryOrder;
import cn.gcjxsc.common.data.wx.redpack.*;
import cn.gcjxsc.payment.data.WeChatConfig;
import cn.gcjxsc.payment.data.pay.PayData;
import cn.gcjxsc.payment.data.wx.order.CreateOrder;
import cn.gcjxsc.payment.data.wx.redpack.*;
import cn.gcjxsc.payment.util.WxUtil;
import com.zgxl.common.util.XmlUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.stereotype.Service;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.util.Map;
import java.util.TreeMap;

/**
 * 微信api
 *
 * @author pumbaa
 * @since 1.0
 */
@Service
public class WeChatApiService {

    public static final String SUCCESS = "SUCCESS";
    public static final String FAIL = "FAIL";

    private Logger logger = LoggerFactory.getLogger(WeChatApiService.class);
    /**
     * 微信统一下单操作
     * @param config 配置文件
     * @param payData 支付数据
     * @return
     */
    public CreateOrder.Response createPayOrder(WeChatConfig config, PayData payData){
        Map<String, String> map = new TreeMap<>();
        map.put("appid", config.getAppid());
        map.put("mch_id", config.getMch_id());
        map.put("nonce_str", WxUtil.generateNonceStr());
        map.put("device_info", "WEB");
        map.put("out_trade_no", payData.getSn());
        map.put("total_fee", payData.getAmount()+"");
        map.put("spbill_create_ip", payData.getIp());
        map.put("notify_url", config.getNotifyUrl());
        map.put("trade_type", WxUtil.TRADE_TYPE_JS);
        map.put("body", payData.getBody());
        map.put("attach", payData.getAttach());
        map.put("openid", payData.getOpenid());

        logger.info("notify:{}, {}",payData.getNotify(), config.getNotifyUrl());
        String sign = WxUtil.generateSignature(map, config.getPay_key());
        map.put("sign", sign);

        CreateOrder createOrder = BeanUtils.instantiateClass(CreateOrder.class);
        BeanWrapper beanWrapper = new BeanWrapperImpl(createOrder);
        beanWrapper.setPropertyValues(map);

        try {
            String xml = XmlUtil.convert2xml(createOrder);
//            System.out.println(xml);
            String result = postXml(WxUtil.URI_PAY_ORDER, xml);
//            System.out.println(result);
            return XmlUtil.convertToObject(CreateOrder.Response.class, result);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 关闭订单
     *
     * @param config  配置信息
     * @param tradeNo 交易号
     * @return 返回操作结果
     */
    public CloseOrder.CloseOrderResponse closeOrder(WeChatConfig config, String tradeNo) {
        Map<String, String> map = new TreeMap<>();
        map.put("appid", config.getAppid());
        map.put("mch_id", config.getMch_id());
        map.put("out_trade_no", tradeNo);
        map.put("nonce_str", WxUtil.generateNonceStr());
        String sign = WxUtil.generateSignature(map, config.getPay_key());
        map.put("sign", sign);

        CloseOrder closeOrder = BeanUtils.instantiateClass(CloseOrder.class);
        BeanWrapper beanWrapper = new BeanWrapperImpl(closeOrder);
        beanWrapper.setPropertyValues(map);

        try {
            String xml = XmlUtil.convert2xml(closeOrder);
            String result = postXml(WxUtil.URI_CLOSE_ORDER, xml);

            return XmlUtil.convertToObject(CloseOrder.CloseOrderResponse.class, result);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 查询订单
     *
     * @param config  配置信息
     * @param tradeNo 交易号
     * @return
     */
    public QueryOrder.QueryOrderResponse queryOrder(WeChatConfig config, String tradeNo) {
        try {
            return orderQuery(config, tradeNo, true);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 查询订单
     *
     * @param config  配置信息
     * @param tradeNo 微信交易号
     * @return
     */
    public QueryOrder.QueryOrderResponse queryOrderByTransactionId(WeChatConfig config, String tradeNo) {
        try {
            return orderQuery(config, tradeNo, false);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 发送红包
     *
     * @param config   微信支付配置信息
     * @param data     红包数据
     * @param certPath 证书地址
     * @return
     * @throws Exception
     */
    public SendResponse sendRedPack(WeChatConfig config, SendData data, String certPath) throws Exception {
        Map<String, String> map = new TreeMap<>();
        map.put("mch_id", config.getMch_id());
        map.put("nonce_str", WxUtil.generateNonceStr());
        map.put("wxappid", config.getAppid());

        map.put("mch_billno", data.getTradeNo());
        map.put("send_name", data.getStoreName());
        map.put("re_openid", data.getOpenid());
        map.put("total_amount", data.getAmount() + "");
        map.put("total_num", "1");
        map.put("wishing", data.getWishing());
        map.put("client_ip", data.getClientIp());
        map.put("act_name", data.getActName());
        map.put("remark", data.getRemark());

        String sign = WxUtil.generateSignature(map, config.getPay_key());
        map.put("sign", sign);

        RedPack redPack = BeanUtils.instantiateClass(RedPack.class);
        BeanWrapper beanWrapper = new BeanWrapperImpl(redPack);
        beanWrapper.setPropertyValues(map);

        String xml = XmlUtil.convert2xml(redPack);
        System.out.println(xml);
        String result = postXmlSSL(WxUtil.URI_SEND_RED_PACK, xml, config.getMch_id(), certPath);
        System.out.println(result);
        return XmlUtil.convertToObject(SendResponse.class, result);
    }

    /**
     * 查询红包数据
     * @param config 微信配置
     * @param sn 订单号
     * @param certPath 证书地址
     * @return
     * @throws Exception
     */
    public QueryRedPackResponse queryRedPack(WeChatConfig config, String sn, String certPath) throws Exception {
        Map<String, String> map = new TreeMap<>();
        map.put("mch_id", config.getMch_id());
        map.put("nonce_str", WxUtil.generateNonceStr());
        map.put("appid", config.getAppid());
        map.put("mch_billno", sn);
        map.put("bill_type", "MCHT");

        String sign = WxUtil.generateSignature(map, config.getPay_key());
        map.put("sign", sign);

        QueryRedPack redPack = BeanUtils.instantiateClass(QueryRedPack.class);
        BeanWrapper beanWrapper = new BeanWrapperImpl(redPack);
        beanWrapper.setPropertyValues(map);

        String xml = XmlUtil.convert2xml(redPack);
        String result = postXmlSSL(WxUtil.URI_QUERY_RED_PACK, xml, config.getMch_id(), certPath);
        return  XmlUtil.convertToObject(QueryRedPackResponse.class, result);
    }

    /**
     * 红包发送是否成功
     *
     * @param response 发送返回数据
     * @return
     */
    public boolean isResponseSuccess(QueryResponse response) {
        return SUCCESS.equalsIgnoreCase(response.getReturn_code()) && SUCCESS.equalsIgnoreCase(response.getResult_code());
    }


    /**
     * 查询订单支付状态
     *
     * @param config  配置信息
     * @param tradeNo 交易号
     * @param isSelf  是否为本系统交易号.如果为false, 则查询微信交易号
     * @return
     * @throws Exception
     */
    private QueryOrder.QueryOrderResponse orderQuery(WeChatConfig config, String tradeNo, boolean isSelf) throws Exception {
        Map<String, String> map = new TreeMap<>();
        map.put("appid", config.getAppid());
        map.put("mch_id", config.getMch_id());
        map.put("out_trade_no", tradeNo);
        map.put("nonce_str", WxUtil.generateNonceStr());
        if (isSelf) {
            map.put("out_trade_no", tradeNo);
        } else {
            map.put("transaction_id", tradeNo);
        }
        String sign = WxUtil.generateSignature(map, config.getPay_key());
        map.put("sign", sign);

        QueryOrder queryOrder = BeanUtils.instantiateClass(QueryOrder.class);
        BeanWrapper beanWrapper = new BeanWrapperImpl(queryOrder);
        beanWrapper.setPropertyValues(map);

        String xml = XmlUtil.convert2xml(queryOrder);
        String result = postXml(WxUtil.URI_QUERY_ORDER, xml);
        return XmlUtil.convertToObject(QueryOrder.QueryOrderResponse.class, result);

    }


    public String postXmlSSL(String uri, String xml, String pwd, String certPath) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        FileInputStream inputStream = new FileInputStream(new File(certPath));
        keyStore.load(inputStream, pwd.toCharArray());
        inputStream.close();

        SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keyStore, pwd.toCharArray()).build();
        SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslContext, new String[]{"TLSv1"}, null, new NoopHostnameVerifier());
        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(factory).build();
        HttpPost post = new HttpPost(uri);
        post.setEntity(new StringEntity(xml, "utf-8"));
        CloseableHttpResponse response = httpClient.execute(post);
        return EntityUtils.toString(response.getEntity(), "utf-8");
    }


    public String postXml(String uri, String xml) throws Exception {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost post = new HttpPost(uri);
        post.setEntity(new StringEntity(xml, "utf-8"));
        CloseableHttpResponse response = httpClient.execute(post);
        return EntityUtils.toString(response.getEntity(), "utf-8");
    }
}
